package com.leonzhangxf.spel.reference;

import com.leonzhangxf.spel.reference.bean.Inventor;
import com.leonzhangxf.spel.reference.bean.PlaceOfBirth;
import com.leonzhangxf.spel.reference.bean.Society;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * Properties, Arrays, Lists, Maps, and Indexers expression.
 *
 * @author leonzhangxf
 * @date 20190427
 */
public class PropertiesArraysListsMapsIndexers {

    public static void main(String[] args) {
        SpelParserConfiguration config = new SpelParserConfiguration(true, true);
        ExpressionParser parser = new SpelExpressionParser(config);
        EvaluationContext evaluationContext = SimpleEvaluationContext.forReadWriteDataBinding()
            .build();

        ZonedDateTime zonedDateTime = LocalDate.of(1856, 1, 1)
            .atStartOfDay(ZoneId.systemDefault());
        Inventor inventor = new Inventor("Nikola Tesla",
            Date.from(Instant.from(zonedDateTime)), "China");
        PlaceOfBirth placeOfBirth = new PlaceOfBirth("Beijing", "China");
        inventor.setPlaceOfBirth(placeOfBirth);
        inventor.setInventions(new String[]{"Maggie", "Agenda", "Wireless communication", "Induction motor"});

        demo1(parser, inventor, evaluationContext);

        System.out.println("============================Members List");
        // Members List
        Society society = new Society();
        society.getMembers().add(inventor);

        demo2(parser, evaluationContext, society);

        System.out.println("============================Officer's Dictionary");
        // Officer's Dictionary
        Inventor pupinInventor = new Inventor("pupin", "Russia");
        pupinInventor.setPlaceOfBirth(new PlaceOfBirth("Idvor"));
        society.getOfficers().put(Society.PRESIDENT, pupinInventor);

        Inventor coutryInventor = new Inventor("John", "America");
        coutryInventor.setPlaceOfBirth(new PlaceOfBirth("LA"));
        society.getOfficers().put(Society.ADVISORS, Collections.singletonList(coutryInventor));

        demo3(parser, evaluationContext, society);
    }

    @SuppressWarnings("unchecked")
    private static void demo3(ExpressionParser parser, EvaluationContext evaluationContext, Society society) {
        Inventor pupin = parser.parseExpression("Officers['president']").getValue(
            evaluationContext, society, Inventor.class);
        System.out.println(pupin);

        // evaluates to "Idvor"
        String cityOfPlaceOfBirth = parser.parseExpression("Officers['president'].PlaceOfBirth.City")
            .getValue(evaluationContext, society, String.class);
        System.out.println(cityOfPlaceOfBirth);

        // setting values
        parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(
            evaluationContext, society, "Croatia");
        System.out.println(((List<Inventor>) society.getOfficers()
            .get(Society.ADVISORS)).get(0).getPlaceOfBirth().getCountry());
    }

    private static void demo2(ExpressionParser parser, EvaluationContext evaluationContext, Society society) {
        // evaluates to "Nikola Tesla"
        String name = parser.parseExpression("Members[0].Name").getValue(
            evaluationContext, society, String.class);
        System.out.println(name);

        // List and Array navigation
        // evaluates to "Wireless communication"
        String inventionOfMembers = parser.parseExpression("Members[0].Inventions[2]").getValue(
            evaluationContext, society, String.class);
        System.out.println(inventionOfMembers);
    }

    private static void demo1(ExpressionParser parser, Inventor inventor, EvaluationContext evaluationContext) {
        // evals to 1856
        Integer year = parser.parseExpression("Birthdate.Year + 1901")
            .getValue(evaluationContext, inventor, Integer.class);
        System.out.println(year);

        // evaluates to "Beijing"
        String city = parser.parseExpression("placeOfBirth.City")
            .getValue(evaluationContext, inventor, String.class);
        System.out.println(city);

        // evaluates to "Induction motor"
        String invention = parser.parseExpression("inventions[3]").getValue(
            evaluationContext, inventor, String.class);
        System.out.println(invention);
    }
}
